package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
)

// 加密的示例
func main() {
	var info = []byte("super secret message")

	// 使用接收方的公钥加密
	publicKey := initPubKey()
	// 加密
	encryptedBytes, err := Encrypt(publicKey, info)
	if err != nil {
		panic(err)
	}
	fmt.Println("encrypted bytes: ", encryptedBytes)

	// 接收方用私钥进行解密
	privateKey := initPriKey()
	// 解密
	decryptedBytes, err := Decrypt(privateKey, encryptedBytes)
	if err != nil {
		panic(err)
	}
	fmt.Println("decrypted message: ", string(decryptedBytes))
}

func initPubKey() *rsa.PublicKey {
	pubCert, err := ioutil.ReadFile("./client/c101.cert.pem")
	if err != nil {
		fmt.Println(err)
		return nil
	}
	publicKey, err := getPublicKeyFromCert(pubCert)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return publicKey
}

func initPriKey() *rsa.PrivateKey {
	priPem, err := ioutil.ReadFile("./client/c101.key.pem")
	if err != nil {
		fmt.Println(err)
		return nil
	}
	privateKey, err := getPrivateKey(priPem)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return privateKey
}

func getPublicKeyFromCert(cert []byte) (publicKey *rsa.PublicKey, err error) {
	block, _ := pem.Decode(cert)

	certificate, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		fmt.Println(err)
		return
	}

	publicKey = certificate.PublicKey.(*rsa.PublicKey)
	return
}

func getPrivateKey(cert []byte) (privateKey *rsa.PrivateKey, err error) {
	block, _ := pem.Decode(cert)

	privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	return
}

// Encrypt 加密算法
// 调用rsa.EncryptOAEP实现具体的加密逻辑，
//  需要提供公钥以及加密的消息内容
func Encrypt(pub *rsa.PublicKey, msg []byte) (encryptedBytes []byte, err error) {
	encryptedBytes, err = rsa.EncryptOAEP(
		sha256.New(),
		rand.Reader,
		pub,
		msg,
		nil)
	return
}

// Decrypt 解密算法
// 调用rsa.DecryptOAEP实现解密逻辑，
// 需要提供私钥以及加密后的内容
func Decrypt(pri *rsa.PrivateKey, msg []byte) (encryptedBytes []byte, err error) {
	encryptedBytes, err = rsa.DecryptOAEP(
		sha256.New(),
		rand.Reader,
		pri,
		msg,
		nil)
	return
}
